﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

/*!
 * \file GcmpStatisticsTool.h
 *
 * \author: shenz
 * \date: 2022.08.10
 * \summary: 用于计数和时间统计，结果分别输出到exe所在的目录下的两个txt文件里(计数CountStatistic.txt，时间TimeStatistic.txt)。支持多线程。计时器精度微秒，输出按照毫秒。
 * \不要在循环过程中进行Output，否则影响性能。
 * \启动软件后需要打开DebugMode里的【其他】->【计数统计工具】和【其他】->【时间统计工具】才能开启对应的统计。
 * \根据需要使用本头文件最后列出的计数器宏和计时器宏即可。
 * \如果计时器嵌套开启，以第一次开启和最后配对的关闭作为计时周期
 */
#pragma once
#include "GcmpDevService.h"
#include <unordered_map>
#include <mutex>

#ifdef _WIN32
  #include <fstream>
  #include <iostream>
#else
#endif

namespace gcmp
{
    class GCMP_DEV_SERVICE_EXPORT GcmpStatisticsTool
    {
    public:
        virtual void Output(const std::string &strTag, bool bClear) = 0;    
    protected:
        std::mutex  m_Mutex;
#ifdef _WIN32
        std::ofstream m_fileStream;
#else
        //TODO: shenz
#endif
    };

    class GcmpStatisticsCountItem
    {
    public:
        GcmpStatisticsCountItem();
        void AddCount(int nCount);
        void Reset();
        int Count() { return m_nCount; }
    private:
        int m_nCount;
    };

    //辅助计数统计工具
    class GCMP_DEV_SERVICE_EXPORT GcmpCountStatisticsTool : public GcmpStatisticsTool
    {
    public:
        static GcmpCountStatisticsTool &instance();

        void AddCount(const std::string & strName, int nCount);        
        void ResetCount(const std::string &strName);

        virtual void Output(const std::string &strTag, bool bClear) override final;
    private:
        ~GcmpCountStatisticsTool();

        GcmpCountStatisticsTool();
        GcmpCountStatisticsTool(const GcmpCountStatisticsTool &);
        GcmpCountStatisticsTool &operator =(const GcmpCountStatisticsTool);

        GcmpStatisticsCountItem *GetCountItem(const std::string & strName);
        void Clear();

        std::unordered_map<std::string, GcmpStatisticsCountItem *> m_CountHash;
    };

    class GcmpTimeStatisticsTool;
    
    class GcmpStatisticsTimeItem
    {
    public:
        GcmpStatisticsTimeItem(unsigned int nThreshold);

        void TimerOn();
        void TimerOff(int64_t nTag);

        double Duration();
        size_t Count() { return m_vecDuration.size(); }
        const std::vector<std::pair<double, int64_t> > & VecDuration() { return m_vecDuration; }
        unsigned int Threshold() { return m_nThreshold; }
    private:
        unsigned int m_nOnCount;               //计时器开启次数
        unsigned int m_nThreshold;  //阈值，用于输出时过滤，不影响计时，单位微秒
        std::chrono::time_point<std::chrono::steady_clock> m_Time;
        std::vector<std::pair<double, int64_t> > m_vecDuration;   //持续时间，单位微秒
    };

    class GcmpStatisticsTimerGroup
    {
    public:
        ~GcmpStatisticsTimerGroup();

        void TimerOn(const std::string &strName, unsigned int nThreshold);
        void TimerOff(const std::string &strName, int64_t nTag);
    private:
        friend class GcmpTimeStatisticsTool;
        GcmpStatisticsTimeItem * getTimeItem(const std::string &strName, unsigned int nThreshold);

        std::unordered_map<std::string, GcmpStatisticsTimeItem*> m_TimerHash;  //Key--计时器名字
    };

    //辅助时间统计工具
    class GCMP_DEV_SERVICE_EXPORT GcmpTimeStatisticsTool: public GcmpStatisticsTool
    {
    public:
        static GcmpTimeStatisticsTool &instance();       

        void TimerOn(const std::string &strName, unsigned int nThreshold = 0);   //开启计时器
        void TimerOff(const std::string &strName, int64_t nTag = 0);  //关闭计时器

        virtual void Output(const std::string &strTag, bool bClear) override final;
    private:
        ~GcmpTimeStatisticsTool();

        GcmpTimeStatisticsTool();
        GcmpTimeStatisticsTool(const GcmpTimeStatisticsTool &);
        GcmpTimeStatisticsTool &operator =(const GcmpTimeStatisticsTool);

        GcmpStatisticsTimerGroup *GetTimerGroup();
        void Clear();

        std::unordered_map<unsigned long, GcmpStatisticsTimerGroup *> m_TimerHash;  //Key--ThreadId
    };

    //辅助类，配合{}使用，类似QP_FUNC，简化代码
//     class GCMP_DEV_SERVICE_EXPORT GcmpTimerObject
//     {
//     public:
//         GcmpTimerObject(const std::string &strName, unsigned int nThreshold)
//         {
//             m_strName = strName;
//             GcmpTimeStatisticsTool::instance().TimerOn(strName, nThreshold);
//         }
//         ~GcmpTimerObject()
//         {
//             GcmpTimeStatisticsTool::instance().TimerOff(m_strName);
//         }
//     private:
//         std::string m_strName;
//     };
}

/************************************************************************/
/* 计数器宏                                                             */
/************************************************************************/
//给名为NAME的计数器增加COUNT个计数，NAME是std::string类型，大小写严格
#define ADDCOUNT(NAME, COUNT) \
    GcmpCountStatisticsTool::instance().AddCount(NAME, COUNT);

//重置名为NAME的计数器到0，NAME是std::string类型，大小写严格
#define RESETCOUNT(NAME) \
    GcmpCountStatisticsTool::instance().ResetCount(NAME);

//输出计数器信息，TAG是std::string类型，输出后不重置计数器。
#define OUTPUTCOUNT(TAG) \
    GcmpCountStatisticsTool::instance().Output(TAG, false);

//输出计数器信息并清除原有计数器，TAG是std::string类型
#define OUTPUTCOUNTANDCLEAR(TAG) \
    GcmpCountStatisticsTool::instance().Output(TAG, true);

/************************************************************************/
/* 普通计时器宏，按线程区分，按次进行统计，并统计调用次数               */
/************************************************************************/
//启动名为NAME的计时器，NAME是std::string类型，大小写严格
#define TIMERON(NAME) \
    GcmpTimeStatisticsTool::instance().TimerOn(NAME);

//启动名为NAME的计时器，NAME是std::string类型，大小写严格，THRESHOLD是时间阈值，无符号整型，单位微秒，低于阈值的会计入总时间，但不进行输出。阈值只有第一次TimeOn的才有效。
#define TIMERWITHTHRESHOLDON(NAME, THRESHOLD) \
    GcmpTimeStatisticsTool::instance().TimerOn(NAME, THRESHOLD);

//关闭名为NAME的计时器，NAME是std::string类型，大小写严格，TAG是int64_t类型，一般放置ID或者0
#define TIMEROFF(NAME, TAG) \
    GcmpTimeStatisticsTool::instance().TimerOff(NAME, TAG);

//输出计时器信息，TAG是std::string类型，代表本次输出的标签。输出后不重置计时器。
#define OUTPUTTIME(TAG) \
    GcmpTimeStatisticsTool::instance().Output(TAG, false);

//输出计时器信息并清除原有计时器，TAG是std::string类型，代表本次输出的标签。
#define OUTPUTTIMEANDCLEAR(TAG) \
    GcmpTimeStatisticsTool::instance().Output(TAG, true);

//创建名字为TIMERNAME的计时器并启动计时，其变量名为OBJNAME
// #define TIMEROBJ(OBJNAME, TIMERNAME, THRESHOLD) \
//     GcmpTimerObject OBJNAME(TIMERNAME, THRESHOLD);
